
import 'package:flame/components.dart';
import 'package:flame/events.dart';
import 'package:klondike/components/operation.dart';
import 'package:klondike/klondike/klondike_world.dart';
import '../../util/effect_utils.dart';
import '../klondike_game.dart';
import '../../components/card.dart';
import '../../components/pile.dart';
import 'tableau_pile.dart';

class WastePile extends PositionComponent
  with TapCallbacks, HasGameReference<KlondikeGame>, HasWorldReference<KlondikeWorld>
  implements Pile {
  
  WastePile({super.position}) : super(size: KlondikeGame.cardSize);

  final List<Card> _cards = [];

  final Vector2 _fanOffset = Vector2(KlondikeGame.cardWidth * 0.2, 0);

  List<Card> getAllCards() {
    return _cards.toList();
  }

  @override
  bool canMoveCard(List<Card> cards) {
    assert(cards.length == 1);
    Card card = cards.first;
    return _cards.isNotEmpty && card == _cards.last;
  }

  @override
  bool canAcceptCard(List<Card> cards) => false;

  @override
  void removeCard(List<Card> cards) {
    for(Card card in cards) {
      assert(card.isFaceUp);
      _cards.remove(card);
    }
  }

  @override
  void acquireCard(List<Card> cards) {
    for(Card card in cards) {
      assert(card.isFaceUp);
      card.position = position;
      card.priority = _cards.length;
      _cards.add(card);
      card.pile = this;
    }
    fanOutTopCards();
  }

  @override
  void returnCard(List<Card> cards) {
    assert(cards.length == 1);
    Card card = cards.first;

    card.priority = _cards.indexOf(card);
    fanOutTopCards();
  }

  @override
  PositionComponent top() => _cards.isNotEmpty ? _cards.last : this;

  void fanOutTopCards() {
    if(game.fanoutNum == 1) {
      return;
    }

    final n = _cards.length;    
    for(var i=0; i<n; i++) {
      _cards[i].position = position;
    }

    if(n==2) {
      _cards[1].position.add(_fanOffset);
    } else if(n>=3) {
      _cards[n-2].position.add(_fanOffset);
      _cards[n-1].position.addScaled(_fanOffset, 2);
    }
  }

  @override
  Future<void> handleTapUp(Card card) async {

    var singleCard = [card];
    
    if(canMoveCard(singleCard)) {
      final suitIndex = singleCard.first.suit.value;
      if (world.foundations[suitIndex].canAcceptCard(singleCard)) {
        Operation operation = Operation.init(
          singleCard,
          this,
          world.foundations[suitIndex],
          [card.position.clone()],
          [world.foundations[suitIndex].position],
          40.0);

        await operation.proceed();
        
        world.histories.add(operation);
      } else {
        bool canMoveToStack = false;
        for(TableauPile tableauPile in world.tableauPiles) {
          if(tableauPile.canAcceptCard(singleCard)) {
            canMoveToStack = true;

            Operation operation = Operation.init(
              singleCard,
              this,
              tableauPile,
              [card.position.clone()],
              tableauPile.fetchPositions(singleCard),
              20.0);
            
            await operation.proceed();
            world.histories.add(operation); // record
            break;
          }
        }
        
        if(!canMoveToStack) {
          EffectUtils.shake(singleCard);
        }
      }
    }
  }
  
  @override
  Future<bool> tryToMove() async {

    if(_cards.isEmpty) {
      return false;
    }

    var singleCard = [_cards.last];    
    if(canMoveCard(singleCard)) {
      final suitIndex = singleCard.first.suit.value;
      if (world.foundations[suitIndex].canAcceptCard(singleCard)) {
        await world.highlight(singleCard, [world.foundations[suitIndex].top()]);
        return true;
      } else {
        for(TableauPile tableauPile in world.tableauPiles) {
          if(tableauPile.canAcceptCard(singleCard)) {
            await world.highlight(singleCard, [tableauPile.top()]);
            return true;
          }
        }
      }
    }

    return false;
  }
}